import logging
import re
from dataclasses import dataclass
from json import loads, dumps

from comp_cab_setup_modules.config import hosts_file_path, ip_expression, setup_hosts_file_path, hostname_expression, \
    suffix
from collections import OrderedDict

from comp_cab_setup_modules.system import get_mac_address


@dataclass
class Host:
    name: str
    hostname: str = ""
    mac_address: str = ""
    student_login: str = ""
    student_pass: str = ""
    admin_login: str = ""
    admin_pass: str = ""
    setup: bool = False
    remote_login: bool = False
    checked: bool = True

    def to_dict(self):
        return {
            "name": self.name,
            "hostname": self.hostname,
            "mac_address": self.mac_address,
            "student_login": self.student_login,
            "student_pass": self.student_pass,
            "admin_login": self.admin_login,
            "admin_pass": self.admin_pass,
            "setup": self.setup,
            "remote_login": self.remote_login,
            "checked": self.checked
        }


class Hosts:
    def __init__(self):
        self.filename = hosts_file_path
        self.setup_filename = setup_hosts_file_path
        with open(self.filename, 'r', encoding='utf-8') as file:
            if not file.read():
                self.clean()
        self.hosts: OrderedDict = self._read()
        self.setup_hosts: dict = self._read_setup()

    def __len__(self) -> int:
        """
        Получение количества хостов
        :return: размер словаря
        """
        return len(self.hosts)

    def __getitem__(self, key: str) -> Host:
        """
        Получение элемента
        :param key: ключ
        :return: элемент Host
        """
        return Host(
            hostname=self.hosts[key]['hostname'],
            mac_address=self.hosts[key]['mac_address'],
            name=self.hosts[key]['name'],
            student_login=self.hosts[key]['student_login'],
            student_pass=self.hosts[key]['student_pass'],
            admin_login=self.hosts[key]['admin_login'],
            admin_pass=self.hosts[key]['admin_pass'],
            setup=self.hosts[key]['setup'],
            remote_login=self.hosts[key]['remote_login'] if 'remote_login' in self.hosts[key].keys() else False,
            checked=self.hosts[key]['checked']
        )

    def __delitem__(self, key: str):
        """
        Удаление элемента и перезапись файла
        @param key: имя даляемого хоста
        @return: все хосты без удалённого
        """
        del self.hosts[key]
        self._write_hosts(self.hosts)
        return self

    def _read(self):
        """
        Чтение данных из файла
        :return: упорядоченный словарь хостов или None, если файла нет
        """
        try:
            with open(self.filename, 'r', encoding='utf-8') as file:
                return OrderedDict(loads(file.read()))
        except FileNotFoundError:
            logging.info('[error] Файл ' + self.filename + ' не найден')
            return None

    def _read_setup(self) -> dict:
        """
        Считывает из файла словарь настроенных ранее хостов
        @return: словарь настроенных ранее хостов
        """
        try:
            with open(self.setup_filename, 'r', encoding='utf-8') as file:
                res = loads(file.read())
                # для перехода на 3 версию программы - новый формат файла setup_hosts.json
                if 'remote_login' not in res.keys():
                    res = {
                        'setup': res,
                        'remote_login': dict()
                    }
                    for key in res['setup']:
                        res['remote_login'][key] = False
                        for host in self.hosts:
                            if self.hosts[host]['hostname'] == key and 'remote_login' in self.hosts[host].keys():
                                res['remote_login'][key] = self.hosts[host]['remote_login']
                    self.setup_hosts = res
                    self._write_setup_hosts(self.setup_hosts)
                return res
        except FileNotFoundError:
            logging.info('[error] Файл ' + self.setup_filename + ' не найден')
            res = {
                'setup': dict(),
                'remote_login': dict()
            }
            self.setup_hosts = res
            self._write_setup_hosts(self.setup_hosts)
            return res

    def _write_hosts(self, hosts: dict):
        """
        Запись данных в файл
        @param hosts: словарь для записи
        @return: None, если запись не удалась
        """
        try:
            with open(self.filename, 'w', encoding='utf-8') as file:
                file.write(dumps(hosts, indent=4, ensure_ascii=False))
        except KeyError:
            logging.info('[error] Ключ не найден')
            return None

    def _write_setup_hosts(self, setup_hosts: dict):
        """
        Запись данных в файл
        @param setup_hosts: словарь для записи
        @return: None, если запись не удалась
        """
        try:
            with open(self.setup_filename, 'w', encoding='utf-8') as file:
                file.write(dumps(setup_hosts, indent=4, ensure_ascii=False))
        except KeyError:
            logging.info('[error] Ключ не найден')
            return None

    def clean(self):
        """
        Очистка файла и словаря текущих хостов
        """
        self.hosts = OrderedDict({})
        self._write_hosts(self.hosts)
        self.hosts: OrderedDict = self._read()

    def set_item(self, key: str, hostname: str = '', mac_address: str = '',
                 student_login: str = '', student_pass: str = '', admin_login: str = '',
                 admin_pass: str = '', setup: bool = False, remote_login: bool = False, checked: bool = True):
        """
        добавить/изменить элемент
        @param key: имя хоста
        @param hostname: адрес хоста (IP или hostname)
        @param mac_address: mac-адрес хоста
        @param student_login: логин ученика на хосте
        @param student_pass: пароль студента на хосте
        @param admin_login: логин админа на хосте
        @param admin_pass: пароль админа на хосте
        @param setup: настроен ли ssh на хосте (bool)
        @param remote_login: настроен ли удалённый вход в систему на хосте (bool)
        @param checked: отмечен ли хост в таблице (bool)
        @return: все хосты после добавления/изменения элемента
        """
        hostname = hostname.replace(' ', '').strip()
        if not hostname:
            host = Host(hostname="", mac_address=mac_address, name=key,
                        student_login=student_login, student_pass=student_pass,
                        admin_login=admin_login, admin_pass=admin_pass, setup=setup,
                        remote_login=remote_login, checked=checked)
        elif re.match(ip_expression, hostname):
            host = Host(hostname=hostname, mac_address=mac_address, name=key,
                        student_login=student_login, student_pass=student_pass,
                        admin_login=admin_login, admin_pass=admin_pass, setup=setup,
                        remote_login=remote_login, checked=checked
                        )
        elif hostname.endswith(suffix):
            hostname = hostname.replace(suffix, '') + suffix
            host = Host(hostname=hostname, mac_address=mac_address, name=key,
                        student_login=student_login, student_pass=student_pass,
                        admin_login=admin_login, admin_pass=admin_pass, setup=setup,
                        remote_login=remote_login, checked=checked
                        )
        else:
            host = Host(hostname=f"{hostname}{suffix}", mac_address=mac_address, name=key,
                        student_login=student_login, student_pass=student_pass,
                        admin_login=admin_login, admin_pass=admin_pass, setup=setup,
                        remote_login=remote_login, checked=checked
                        )
        self.hosts[key] = host.to_dict()
        if re.match(hostname_expression, hostname) or (hostname.endswith(suffix) and hostname != suffix):
            self.setup_hosts['setup'][hostname] = setup
            self.setup_hosts['remote_login'][hostname] = remote_login
        self._write_hosts(self.hosts)
        self._write_setup_hosts(self.setup_hosts)
        return self

    def to_list(self) -> [str]:
        """
        Получение имён хостов в виде списка
        :return: список имён хостов
        """
        result = []
        for host in self.hosts:
            result.append(self.hosts[host]['name'])
        return result

    def items_to_list(self) -> [Host]:
        """
        Получение хостов в виде списка
        :return: список объектов Host
        """
        result = []
        for host in self.hosts:
            result.append(
                Host(
                    hostname=self.hosts[host]['hostname'],
                    mac_address=self.hosts[host]['mac_address'],
                    name=self.hosts[host]['name'],
                    student_login=self.hosts[host]['student_login'],
                    student_pass=self.hosts[host]['student_pass'],
                    admin_login=self.hosts[host]['admin_login'],
                    admin_pass=self.hosts[host]['admin_pass'],
                    setup=self.hosts[host]['setup'],
                    remote_login=self.hosts[host]['remote_login'] if 'remote_login' in self.hosts[host].keys()
                    else False,
                    checked=self.hosts[host]['checked']
                )
            )
        return result

    def set_true_setup(self, key: str) -> None:
        """
        Запоминает, что данный хост был настроен
        @param key: имя хоста
        """
        self.hosts[key]['setup'] = True
        self.hosts[key]['mac_address'] = get_mac_address(self.__getitem__(key))
        hostname = self.hosts[key]['hostname']
        # TODO это будет работать?
        if re.match(hostname_expression, hostname) or (hostname.endswith(suffix) and hostname != suffix):
            self.setup_hosts['setup'][self.hosts[key]['hostname']] = True
            if self.hosts[key]['hostname'] not in self.setup_hosts['remote_login'].keys():
                self.setup_hosts['remote_login'][self.hosts[key]['hostname']] = False
        # self.setup_hosts['setup'][self.hosts[key]['hostname']] = True
        self._write_hosts(self.hosts)
        self._write_setup_hosts(self.setup_hosts)

    def set_false_setup(self, key: str) -> None:
        """
        Запоминает, что данный хост не был настроен
        @param key: имя хоста
        """
        self.hosts[key]['setup'] = False
        hostname = self.hosts[key]['hostname']
        if re.match(hostname_expression, hostname) or (hostname.endswith(suffix) and hostname != suffix):
            self.setup_hosts['setup'][self.hosts[key]['hostname']] = False
            if self.hosts[key]['hostname'] not in self.setup_hosts['remote_login'].keys():
                self.setup_hosts['remote_login'][self.hosts[key]['hostname']] = False
        self._write_hosts(self.hosts)
        self._write_setup_hosts(self.setup_hosts)

    def set_checked(self, hostname: str, check_status) -> None:
        """
        Записывает в файл, был ли данный хост отмечен в таблице
        @param hostname: имя хоста
        @param check_status: был ли хост отмечен
        """
        self.hosts[hostname]['checked'] = check_status
        self._write_hosts(self.hosts)

    def set_ip(self, hostname: str, ip: str) -> None:
        """
        Сохраняет в файл IP указанного хоста
        @param hostname: имя хоста
        @param ip: IP хоста
        """
        self.hosts[hostname]['hostname'] = ip
        self._write_hosts(self.hosts)

    def set_student_login(self, hostname: str, login: str) -> None:
        """
        Записывает в файл новый логин ученика
        @param hostname: имя хоста
        @param login: указанный логин
        """
        self.hosts[hostname]['student_login'] = login
        self._write_hosts(self.hosts)

    def set_student_pass(self, hostname: str, password: str) -> None:
        """
        Записывает в файл новый пароль ученика
        @param hostname: имя хоста
        @param password: указанный пароль
        """
        self.hosts[hostname]['student_pass'] = password
        self._write_hosts(self.hosts)

    def set_true_remote_login_setup(self, hostname: str) -> None:
        """
        Записывает в файлы, включена ли на хосте возможность удалённого входа в систему
        @param hostname: имя хоста
        """
        self.hosts[hostname]['remote_login'] = True
        self.setup_hosts['remote_login'][self.hosts[hostname]['hostname']] = True
        self._write_hosts(self.hosts)
        self._write_setup_hosts(self.setup_hosts)
